package us.wsu.compnet.reliabletransfer;

import java.io.*;   
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class implements a receiver of the Stop-And-Wait reliable transfer protocol
 */
public class MyServerSocket implements Runnable
{
	
	private PipedOutputStream internalOutputStream; // Stream to which we write data for the upper layer
	private PipedInputStream upperLayerStream; // Stream given to upper layer, connected to internalOutputStrea
        //var hold sequence number
        int sequence_number_s32;
        DatagramSocket serverSocket;
        DatagramPacket ackPacket;
        

	//?
	
	/**
	 * Creates a receiver socket on a given port. 
	 */
	public MyServerSocket(int port) throws SocketException
	{
		// Open the UDP socket.?
		serverSocket = new DatagramSocket(port);
                
                //starting value for sequence number                
                sequence_number_s32 = -1;	
		
		// Create stream to the upper layer.
		internalOutputStream = new PipedOutputStream();
		try {
			upperLayerStream = new PipedInputStream(internalOutputStream);
		} catch (IOException e)
                {
                    //This should not happen.
                    Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, e);
		}
		
		// Start thread that will receive the data packets and reply with acks.
		(new Thread(this)).start();
	}

	/**
	 * Returns the InputStream associated with the socket. 
	 */
	public InputStream getInputStream() {
		return upperLayerStream;
                
	}

	/**
	 * Implementation of the receiver protocol.
	 */
        @Override
	public void run()
	{
            //array to hold new packet
		byte[] receiveData = new byte[MyDataPacket.MAX_SIZE];
                //array to hold just the payload "data" of packet
                byte[] payload = new byte[MyDataPacket.MAX_PAYLOAD_SIZE];
                //array to hold the data that will go in the ack packet contains 4 byte int
                byte[] ackArray = new byte[4];
		//place to receive datagram object.
		DatagramPacket receivePacket;
                //ini datagram object
                receivePacket = new DatagramPacket(receiveData, receiveData.length);
          while(true)
          {
		
            try {
                    //attempt to fill receivePacket with data from the socket
                //receive blocks until data is available
                    serverSocket.receive(receivePacket);
                } catch (IOException ex)
                {
                Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
                }
                //create object that has methods to parse the packet
                MyDataPacket data = null;
            try
            {
                //ini with the data received from the socket
                data = new MyDataPacket(receivePacket.getData(), receivePacket.getLength());
            } catch (IOException ex)
            {
                //what happens if this does occur Program crash??
                Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
            }
            // Extract sequence number and data payload.
                //check if sequence number is different if yes then have new packet
                if(sequence_number_s32 != data.getSeqNum())
                {
                    //update to new sequence_number
                    sequence_number_s32 = data.getSeqNum();
                    //grab the data stored in the packet
                    payload = data.getData();
                    
                    try
                    {
                        //write what data is in the packet to screen
                       internalOutputStream.write(payload);
                       internalOutputStream.flush();
                    } catch (IOException ex) {
                        Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    
                    //convert an int to a byte [] array
                     ackArray = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).order(ByteOrder.BIG_ENDIAN).putInt(sequence_number_s32).array();
                     //create the packet that holds the ack
                    ackPacket = new DatagramPacket(ackArray, ackArray.length, receivePacket.getAddress(), receivePacket.getPort());
                    try
                    { 
                        //and away she goes! to never be hear from again??
                        //tune in next loop same packet channel same packet time!
                        serverSocket.send(ackPacket);
                    } catch (IOException ex)
                    {
                        Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                else
                {
                    //packet is a duplicate and we just send back an ACK so we will get a new packet
                    ackArray = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).order(ByteOrder.BIG_ENDIAN).putInt(sequence_number_s32).array();
                    ackPacket = new DatagramPacket(ackArray, ackArray.length, receivePacket.getAddress(), receivePacket.getPort());
                    try
                    { 
                        //Hi Ho Silver away! send out ack packet.
                        serverSocket.send(ackPacket);
                    } catch (IOException ex)
                    {
                        Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    
                }
                    
}

}
}
